
#include "basespikingchannel.h"


//********* Code for BaseSpikingChannel::OneCell ***********************

BaseSpikingChannel::OneCell::OneCell() : IntegratorNeuron_cpm(), xOffset(0), yOffset(0)
{}


//********* Code for BaseSpikingChannel::SamplingSheme *****************

BaseSpikingChannel::SamplingScheme::SamplingScheme() : father(0), done(false)
{}


//*********    Code for BaseSpikingChannel   ***************************

BaseSpikingChannel::BaseSpikingChannel(double stepp) : sampScheme(0), done(false), radScheme(0)
{
  step=stepp;
  ratiopix=1;
  
  g_leak=50;
  tau_refr=0.003;
  noise_refr=0.0;
  noiseV=0.0;
  randomInit=false;
}


BaseSpikingChannel::~BaseSpikingChannel(void)
{
  for(uint i=0;i<units.size();++i) 
    delete[] units[i];
  delete sampScheme;
}


void BaseSpikingChannel::allocateValues(void)
{
	if(sampScheme && (!sampScheme->done))  
	  sampScheme->allocateValues();

	if(!units.size()) //meaning that units have not been loaded yet from an xml file
	{
	  if(!sampScheme)
	  {  
      cerr<<"Error in BaseSpikingChannel: No sampling scheme was defined!"<<endl;
      exit(0);
    }
    sampScheme->createUnits(); // create all new units
  }
	done=true;
}


BaseSpikingChannel::OneCell* BaseSpikingChannel::create_new_unit()
{
  return new OneCell();
}


void BaseSpikingChannel::initialize_new_unit(double xx, double yy)
{
  units.push_back(create_new_unit());

  units.back()->set_g_leak(g_leak); //spiking properties from class ConductanceNeuron
  units.back()->tau_refr=this->tau_refr;
  units.back()->noise_refr=this->noise_refr;
  units.back()->set_noiseV(noiseV);
  units.back()->set_step(step); //the IntegratorNeuron parameter

  units.back()->xOffset=xx;  //OneCell properties
  units.back()->yOffset=yy;

  if(randomInit)
	  units.back()->set_V ( ranf() );
}


//Feeding an image to the retina.

void BaseSpikingChannel::feedInput(const CImg<double> & image, bool ImageDisposition)
{
  if (!done)
    allocateValues();
  double xMid=image.width()/2.0, yMid=image.height()/2.0;

  if(ImageDisposition)
      for(uint ind=0;ind<units.size();++ind)
          units[ind]->feedCurrent(image(xMid+units[ind]->xOffset,yMid-units[ind]->yOffset),
          0,true);
  else
  for(uint ind=0;ind<units.size();++ind)
    units[ind]->feedCurrent(
      image.linear_atXY(xMid+ratiopix*units[ind]->xOffset,
                        yMid-ratiopix*units[ind]->yOffset),
      0,true);

}

CImg<double> BaseSpikingChannel::feedInput(const CImg<double> & image, double gamma, double directionAngle, double lambda)
{
  double xImg, yImg, xPos, yPos, pixel_angle;
  CImg<double> temp(image);
  CImg<double> out(image);
  out.fill(0.0);
  double gammaMultiplier;

  if (!done)
    allocateValues();
  double xMid=image.width()/2.0, yMid=image.height()/2.0;

  for(uint ind=0;ind<units.size();++ind){
      xPos = units[ind]->xOffset + xMid;
      yPos = yMid - units[ind]->yOffset;

    temp.fill(0.0);
    cimg_forXY(image,x,y)
     {
        xImg = x - xPos;
        yImg = - ( y - yPos );
        pixel_angle = atan2 (yImg,xImg);
        if (pixel_angle<0 && directionAngle!=0) pixel_angle = 2.0*cimg_library::cimg::PI + pixel_angle;
        if (pixel_angle > (directionAngle+cimg_library::cimg::PI)) pixel_angle -= 2.0*cimg_library::cimg::PI;
        if (directionAngle>cimg_library::cimg::PI/2.0 && pixel_angle<(directionAngle-cimg_library::cimg::PI)) pixel_angle += 2.0*cimg_library::cimg::PI;

        gammaMultiplier = (exp(pow((pixel_angle-directionAngle),2)/(2.0*pow(gamma,2))) / (2.0*(cimg_library::cimg::PI)*pow(gamma,2)));

        temp(x,y) = image(x,y) * gammaMultiplier;

        if (lambda!=0) temp(x,y) *= lambda;
        else temp(xPos,yPos) +=  temp(x,y);
        //if(x%50==0 && y%50==0 && temp(x,y)!=0)
        //cout<<"Gamma multiplier: "<<gammaMultiplier<<", image("<<x<<","<<y<<"): "<<image(x,y)<<", lambda: "<<lambda<<endl<<"Result-> temp("<<x<<","<<y<<"): "<<temp(x,y)<<endl;

    }
    out += temp;
    /*cout<<endl<<"Printting temp"<<endl<<endl;
    temp.print();
    out += temp;
    cout<<endl<<"Printting out"<<endl<<endl;
    out.print();
    cout<<endl<<"Cell numer "<<ind<<" offset:("<<units[ind]->xOffset<<","<<units[ind]->yOffset<<"(xPos,yPos)=("<<xPos<<","<<yPos<<") and position to app filter: ("
       <<xMid+units[ind]->xOffset<<","<<yMid+units[ind]->yOffset<<")"<<endl;*/
    /*units[ind]->feedCurrent(
      temp.linear_atXY(xMid+ratiopix*units[ind]->xOffset,
                        yMid+ratiopix*units[ind]->yOffset),
      0,true);*/

    units[ind]->feedCurrent(
          temp(xPos,yPos),
          0,true);
  }
  return (out);
}


//Channel parameter definition functions :

void BaseSpikingChannel::checkStatus(void)
{
  if(done)
  {
    cerr<<"Error in BaseSpikingChannel: You're trying to change a parameter after it was initialized."<<endl;
    exit(0);
  }
}

BaseSpikingChannel& BaseSpikingChannel::set_step(double s)
{
  checkStatus();
  step=s;
  return *this;
}

BaseSpikingChannel& BaseSpikingChannel::set_ratiopix(double r)
{
  checkStatus();
  ratiopix=r;
  return *this;
}

BaseSpikingChannel& BaseSpikingChannel::set_radScheme(RadialScheme *rad)
{
  checkStatus();
  radScheme=rad;
  return *this;
}

BaseSpikingChannel& BaseSpikingChannel::set_g_leak(double f)
{
  checkStatus();
  g_leak=f;
  return *this;
}
BaseSpikingChannel& BaseSpikingChannel::set_tau_refr(double f)
{
  checkStatus();
  tau_refr=f;
  return *this;
}
BaseSpikingChannel& BaseSpikingChannel::set_noise_refr(double f)
{
  checkStatus();
  noise_refr=f;
  return *this;
}
BaseSpikingChannel& BaseSpikingChannel::set_noiseV(double f)
{
  checkStatus();
  randomInit=f;
  return *this;
}
BaseSpikingChannel& BaseSpikingChannel::set_randomInit(int f)
{
  checkStatus();
  noiseV=f;
  return *this;
}

double BaseSpikingChannel::read_g_leak()
{
    return (g_leak);
}

double BaseSpikingChannel::read_noiseV()
{
    return(noiseV);
}

double BaseSpikingChannel::read_noise_refr()
{
    return(noise_refr);
}

double BaseSpikingChannel::read_tau_refr()
{
    return(tau_refr);
}

int BaseSpikingChannel::read_randomInit()
{
    return(randomInit);
}
